// JNI code for the jfreeglut main class, this JNI links
// to the glut functions. It simply maps Java types to C types
// and back. It also provides static C functions which get 
// pass to glut as pointers to function (callbacks) and 
// when invoked the callback call the JVM via the Java
// invocation interaface so that Java can handle the
// callback. When Java is invoked the callback always
// passes the current glut window id so that the static
// Java function can map the id to the appropriate glutWindow
// instance and hence invoke the appropriate handler instance.

#include "com_chrishobson_jreeglut_glut.h"
#include "JavaString.h"

#include "GL/glut.h"

static JNIEnv* s_Env;
static jclass s_glutWindowClass = 0;
static void SetupFunc(
  JNIEnv* a_Env
, jclass a_glutWindowClass
, jmethodID* a_FuncId
, const char* a_FuncName
, const char* a_FuncSig
)
{
  if(a_glutWindowClass) {
    if(!s_glutWindowClass) {
      s_Env = a_Env;
      s_glutWindowClass = (jclass)s_Env->NewGlobalRef(a_glutWindowClass);
    }
    if(!*a_FuncId) {
      *a_FuncId = a_Env->GetStaticMethodID(s_glutWindowClass, a_FuncName,a_FuncSig);
    }
  }
}

static jmethodID s_glutWindow_MotionId;
static void MotionFunc(
  int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_MotionId
                             , 0, glutGetWindow(), a_X, a_Y);
}
static void PassiveMotionFunc(
  int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_MotionId
                             , 1, glutGetWindow(), a_X, a_Y);
}

static jmethodID s_glutWindow_ReshapeId = 0;
static void ReshapeFunc(
  int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_ReshapeId
                             , glutGetWindow(), a_X, a_Y);
}

static jmethodID s_glutWindow_KeyboardId = 0;
static void KeyboardFunc(
  unsigned char a_Key
, int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_KeyboardId
                             , glutGetWindow(), (jint)a_Key, a_X, a_Y);
}

static jmethodID s_glutWindow_SpecialId = 0;
static void SpecialFunc(
  int a_Key
, int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_SpecialId
                             , glutGetWindow(), (jint)a_Key, a_X, a_Y);
}

static jmethodID s_glutWindow_MouseId = 0;
static void MouseFunc(
  int a_Button
, int a_State
, int a_X
, int a_Y
)
{
  s_Env->CallStaticVoidMethod( s_glutWindowClass, s_glutWindow_MouseId
                             , glutGetWindow(), a_Button, a_State, a_X, a_Y);
}

static jmethodID s_glutWindow_DisplayId = 0;
static void DisplayFunc()
{
  s_Env->CallStaticVoidMethod(s_glutWindowClass, s_glutWindow_DisplayId
                             , glutGetWindow());
}

static jclass s_glutMenuClass = 0;
static jmethodID s_glut_MenuId = 0;
static void MenuFunc(
  int a_MenuValue
)
{
  s_Env->CallStaticVoidMethod(s_glutWindowClass, s_glut_MenuId
                             , glutGetWindow(), glutGetMenu(), a_MenuValue);
}



/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    Init
 * Signature: ([Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutInit(
  JNIEnv *
, jclass
, jobjectArray
)
{
  // This function not implemented yet just init glut
  // with no arguments.
  int l_Num = 0;
  glutInit(&l_Num,0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutInitDisplayMode
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutInitDisplayMode(
  JNIEnv *
, jclass
, jint a_Mode
)
{
  glutInitDisplayMode(a_Mode);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutInitWindowSize
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutInitWindowSize
  (JNIEnv *, jclass, jint x, jint y)
{
  glutInitWindowSize(x, y);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutInitWindowPosition
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutInitWindowPosition
  (JNIEnv *, jclass, jint x, jint y)
{
  glutInitWindowPosition(x, y);
}


/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    CreateWindowJNI
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_chrishobson_jreeglut_glut_glutCreateWindow(
  JNIEnv* a_Env
, jclass
, jstring a_Title
)
{
  return glutCreateWindow(JavaString(a_Env, a_Title));
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutCreateMenu
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_chrishobson_jreeglut_glut_glutCreateMenu(
  JNIEnv*
, jclass
)
{
  return glutCreateMenu(MenuFunc);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutSetMenu
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutSetMenu
  (JNIEnv *, jclass, jint a_MenuId)
{
  glutSetMenu(a_MenuId);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutAddMenuEntry
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutAddMenuEntry(
  JNIEnv* a_Env
, jclass
, jstring a_Text
, jint a_Item
)
{
  glutAddMenuEntry(JavaString(a_Env, a_Text), a_Item);
}

/*
 * Class:     JavaString(a_Env, a_Text), a_Item);
 * Method:    glutAddSubMenu
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutAddSubMenu(
  JNIEnv* a_Env
, jclass
, jstring a_Text
, jint a_Item
)
{
  glutAddSubMenu(JavaString(a_Env, a_Text), a_Item);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutAttachMenu
 * Signature: (Ljava/lang/Class;I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutAttachMenu(
  JNIEnv *
, jclass
, jclass a_glutMenuClass
, jint a_Button
)
{
  if(!s_glutMenuClass) {
    s_glutMenuClass = (jclass)s_Env->NewGlobalRef(a_glutMenuClass);
    s_glut_MenuId = s_Env->GetStaticMethodID(s_glutMenuClass, "Menu","(III)V");
  }
  glutAttachMenu(a_Button);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    MainLoop
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutMainLoop(
  JNIEnv *
, jclass
)
{
  glutMainLoop();
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutPostRedisplay
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutPostRedisplay(
  JNIEnv *
, jclass
)
{
  glutPostRedisplay();
}
/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutSwapBuffers
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutSwapBuffers(
  JNIEnv*
, jclass
)
{
  glutSwapBuffers();
}
/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutWireCube
 * Signature: (D)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutWireCube
  (JNIEnv *, jclass, jdouble size)
{
  glutWireCube(size);
}
/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutGetModifiers
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_chrishobson_jreeglut_glut_glutGetModifiers
  (JNIEnv *, jclass)
{
  return glutGetModifiers();
}

  /*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    glutWireSphere
 * Signature: (DII)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutWireSphere
  (JNIEnv *, jclass, jdouble radius, jint slices, jint stacks)
{
  glutWireSphere(radius, slices, stacks);
}


/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    MotionFunc
 * Signature: (Ljava/lang/Class;)V
 *
 * This method is called via glutWindow.SetMotionHandler to setup
 * a callback for the glutMotionFunc so that the callback can be
 * routed back to Java and thence on to the users MotionHandler
 * for the specified window.
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_MotionFunc(
  JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_MotionId, "Motion","(IIII)V"); 

  glutMotionFunc(a_glutWindowClass ? MotionFunc : 0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    PassiveMotionFunc
 * Signature: (Ljava/lang/Class;)V
 *
 * This method is called via glutWindow.SetPassiveMotionHandler to setup
 * a callback for the glutPassiveMotionFunc so that the callback can be
 * routed back to Java and thence on to the users MotionHandler
 * for the specified window.
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_PassiveMotionFunc(
  JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_MotionId, "Motion","(IIII)V");  

  glutPassiveMotionFunc(a_glutWindowClass ? PassiveMotionFunc : 0);
}
/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    KeyboardFunc
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_KeyboardFunc(
  JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_KeyboardId, "Keyboard","(IIII)V");  

  glutKeyboardFunc(a_glutWindowClass ? KeyboardFunc : 0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    SpecialFunc
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_SpecialFunc(
  JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_SpecialId, "Special","(IIII)V");  

  glutSpecialFunc(a_glutWindowClass ? SpecialFunc : 0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    SetWindow
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_glutSetWindow(
  JNIEnv*
, jclass
, jint a_Id
)
{
	glutSetWindow(a_Id);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    ReshapeFunc
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_ReshapeFunc(
  JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_ReshapeId, "Reshape","(III)V");  

  glutReshapeFunc(a_glutWindowClass ? ReshapeFunc : 0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    DisplayFunc
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_DisplayFunc(
 JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_DisplayId, "Display","(I)V");  

  // Display func can never be cancelled once set, but must be
  // set for each window so always call to set it.
  glutDisplayFunc(DisplayFunc);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    MouseFunc
 * Signature: (Ljava/lang/Class;)V
 */
JNIEXPORT void JNICALL Java_com_chrishobson_jreeglut_glut_MouseFunc(
 JNIEnv* a_Env
, jclass a_glutClass
, jclass a_glutWindowClass
)
{
  SetupFunc(a_Env, a_glutWindowClass, &s_glutWindow_MouseId, "Mouse","(IIIII)V");  

  glutMouseFunc(a_glutWindowClass ? MouseFunc : 0);
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    wglGetCurrentContext
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_com_chrishobson_jreeglut_glut_wglGetCurrentContext
  (JNIEnv *, jclass)
{
  return (jlong)wglGetCurrentContext();
}

/*
 * Class:     com_chrishobson_jreeglut_glut
 * Method:    wglShareLists
 * Signature: (JJ)Z
 */
JNIEXPORT jboolean JNICALL Java_com_chrishobson_jreeglut_glut_wglShareLists
  (JNIEnv *, jclass, jlong main, jlong other)
{
  return wglShareLists((HGLRC)main, (HGLRC)other);
}